﻿using EM.RedisCache.AspNetCore.Options;
using gt.rediscache.core;
using gt.rediscache.logger;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace gt.rediscache.aspnetcore
{
    internal class RedisClientManager : IRedisClientManager
    {
        private readonly ConcurrentDictionary<string, IRedisClient> _clientContainer;
        private readonly IRedisClientFactory _clientFactory;
        private readonly IRedisCacheLogger _redisLogger;
        private readonly RedisClientsOptions _options;

        public RedisClientManager(IOptionsMonitor<RedisClientsOptions> optionsMonitor,
            IRedisClientFactory clientFactory,
            IRedisCacheLogger redisLogger)
        {
            _clientContainer = new ConcurrentDictionary<string, IRedisClient>();
            _clientFactory = clientFactory;
            _redisLogger = redisLogger;
            _options = optionsMonitor.CurrentValue;

            Initialization();
        }

        /// <summary>
        /// 初始化
        /// </summary>
        private void Initialization()
        {
            RedisLogManager.ReplaceLogger(_redisLogger);
            var tasks = _options.ApplicationCaches.Select(appCache =>
            {
                return Task.Run(() =>
                {
                    var client = _clientFactory.CreateClient(appCache.Name);
                    RedisLogManager.Info($"rediscache client {appCache.Name} init success.");
                    TryAddClient(appCache.Name, client);
                });
            }).ToArray();

            Task.WaitAll(tasks);
        }

        /// <summary>
        /// 获取所有得客户端
        /// </summary>
        /// <returns></returns>
        public List<IRedisClient> GetAllClients()
        {
            if (_clientContainer.Values == null || _clientContainer.Values.Count == 0)
                return null;
            return _clientContainer.Values.ToList();
        }
        /// <summary>
        /// 是否存在Client
        /// </summary>
        /// <param name="clientName"></param>
        /// <returns></returns>
        public bool ContainsClient(string clientName)
        {
            return _clientContainer.ContainsKey(clientName);
        }
        /// <summary>
        /// 获取Client
        /// </summary>
        /// <param name="clientName"></param>
        /// <returns></returns>
        public IRedisClient GetClient(string clientName)
        {
            IRedisClient client = null;
            _clientContainer.TryGetValue(clientName, out client);
            return client;
        }

        #region private

        /// <summary>
        /// 新增Client
        /// 重复的clientName 会抛出异常
        /// </summary>
        /// <param name="clientName"></param>
        /// <param name="client"></param>
        private void TryAddClient(string clientName, IRedisClient client)
        {
            if (_clientContainer.ContainsKey(clientName))
                throw new InvalidOperationException($"same clientName:{clientName} for redisclient exist!");
            var result = _clientContainer.TryAdd(clientName, client);
            if (!result)
                throw new Exception($" clientName:{clientName} for redisclient add failed!");
        }
        /// <summary>
        /// 删除并关闭Client
        /// </summary>
        /// <param name="clientName"></param>
        private void CloseClient(string clientName)
        {
            IRedisClient client = null;
            _clientContainer.TryRemove(clientName, out client);
            if (client != null)
            {
                client.Dispose();
            }
        }

        /// <summary>
        /// 删除并关闭所有Client
        /// </summary>
        private void CloseAllClients()
        {
            var clientNames = _clientContainer.Keys;
            if (clientNames != null && clientNames.Count != 0)
            {
                foreach (var name in clientNames)
                {
                    CloseClient(name);
                }
                _clientContainer.Clear();
            }
        }

        #endregion
        public void Dispose()
        {
            CloseAllClients();
        }
    }
}
